import { RequestService } from './request.service';
import { Component, OnInit, EventEmitter, Input, Output, ContentChild, TemplateRef, ElementRef, ViewChild } from '@angular/core';
import { UploadFile, Lifecycle, CommonFile, UploadResponse } from './upload.interface';
import { HttpResponse } from '@angular/common/http';
import { DomSanitizer } from '@angular/platform-browser';

@Component({
  selector: 'el-upload',
  templateUrl: './upload.component.html'
})
export class UploadComponent implements OnInit {
  @Input() set disabled(val: boolean) {
    console.warn('Element Angular: (disabled) is discarded, use [elDisabled] replace it.');
  };
  @Input() data: any = {};
  @Input() name: string = 'file';
  @Input() action: string = '';
  @Input() accept: string = '';
  @Input() drag: boolean = false;
  @Input() multiple: boolean = false;
  @Input() headers: any = {};
  @Input('with-credentials') withCredentials: boolean = false;
  @Input('show-file-list') showFileList: boolean = true;
  @Input('list-type') listType: string = 'text';
  @Input('file-list') fileList: UploadFile[]=[];
  @Input('upload-filter') uploadFilter!: (f: File) => boolean;
  @Output() preview: EventEmitter<CommonFile> = new EventEmitter();
  @Output() remove: EventEmitter<CommonFile> = new EventEmitter();
  @Output() progress: EventEmitter<UploadResponse<any>> = new EventEmitter();
  @Output() success: EventEmitter<UploadResponse<any>> = new EventEmitter();
  @Output() error: EventEmitter<UploadResponse<any>> = new EventEmitter();
  get lifecycle() {
    var _this = this;
    return {
      preview: function (f: any) { return _this.preview.emit(f); },
      remove: function (f: any) { return _this.remove.emit(f); },
      success: function (f: any, res: any, percentage: any) { return _this.success.emit({ commonFile: f, response: res,percentage: percentage  }); },
      error: function (f: any, err: any, percentage: any) { return _this.error.emit({ commonFile: f, error: err ,percentage: percentage }); },
      progress: function (f: any, percentage: any) { return _this.progress.emit({ commonFile: f, percentage: percentage }); },
    };
  }

  private request: any;
  private sanitizer: any;
  @ContentChild('trigger') trigger!: TemplateRef<any>;
  @ContentChild('dragger') dragger!: TemplateRef<any>;
  @ContentChild('tip') tip!: TemplateRef<any>;
  @ViewChild('input') input!: ElementRef;
  files: CommonFile[] = [];

  constructor(request: RequestService, sanitizer: DomSanitizer) {
    this.request = request;
    this.sanitizer = sanitizer;
    this.uploadFilter = function (f) { return true; };
  }

  ngOnInit(): void {
    var _this = this;
    this.request
      .setHeader(this.headers)
      .setCredentials(this.withCredentials)
      .setFileName(this.name)
      .addExtraData(this.data);

    this.fileList.forEach(function (file) {
      _this.files.push({
        id: _this.generateID(),
        name: file.name,
        status: 'success',
        raw: undefined,
        size: 0,
        url: _this.sanitizer.bypassSecurityTrustUrl(file.url),
      });
    });
  }

  generateID() {
    return Math.random().toString(16).substr(2, 8);
  }


  updatePercentage(response: any) {
    var loaded = response.loaded, total = response.total;
    if (loaded === undefined || !total)
      return 0;
    return Math.round(loaded / total * 100);
  }

  clickHandle() {
    if (this.disabled)
      return;
    this.input.nativeElement.click();
  }

  changeHandle(event: Event): void {
    var _this = this;
    var  files:any = (event.target as any).files;
    if (!files || !files.length)
      return;
    var /** @type {?} */ checkedFiles = this.multiple ? Array.from(files) : [files[0]];
    this.input.nativeElement.value = null;
    checkedFiles.forEach(function (file:any) {
      var /** @type {?} */ next = {
        id: _this.generateID(),
        name: file.name,
        status: 'ready',
        size: file.size,
        percentage: 0,
        raw: file,
        url: _this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(file))
      };
      _this.files.push(next);
      _this.updateFile(next);
      _this.uploadFilter(file) === false ? _this.removeHandle(next) : _this.upload(next);
    });
  };
  upload(file: CommonFile): void {
    var _this = this;
    file.status = 'uploading';
    this.updateFile(file);
    this.request.upload(this.action, file.raw)
      .subscribe(function (event:any) {
        file.percentage = _this.updatePercentage(event);
        file.percentage && _this.lifecycle.progress(file, file.percentage);
        if (event instanceof HttpResponse) {
          _this.lifecycle.success(Object.assign(file, { status: 'success' }), event,file.percentage);
        }
        _this.updateFile(file);
      }, function (err:any) {
        file.status = 'fail';
        _this.lifecycle.error(file, err,file.percentage);
        _this.removeHandle(file);
      });
  };
  removeHandle(file: CommonFile): void {
    this.lifecycle.remove(file);
    var /** @type {?} */ index = this.files.findIndex(function (_a) {
      var id = _a.id;
      return file.id === id;
    });
    this.files.splice(index, 1);
  };
  updateFile(file: CommonFile): void {
    var /** @type {?} */ index = this.files.findIndex(function (_a) {
      var id = _a.id;
      return file.id === id;
    });
    if (!index)
      return;
    this.files[index] = file;
  };
}
